package main

import (
	"encoding/json"
	"fmt"
	"github.com/willf/bitset"
	"strconv"
	"strings"
	"time"
)

type BloomFilter struct {
	set       *bitset.BitSet
	startTime time.Time
}

func NewBloomFilter(size uint) *BloomFilter {
	bf := new(BloomFilter)
	bf.set = bitset.New(size)
	t1, err := time.ParseInLocation("2006-01-02 15:04:05", "2019-01-01 00:00:00", time.Local)
	if err != nil {
		t1 = time.Now()
	}
	bf.startTime = t1
	return bf
}
func (bf *BloomFilter) Add(value time.Time) {
	bf.set.Set(bf.Hash(value))
}
func (bf *BloomFilter) Hash(value time.Time) uint {
	diff := value.Unix() - bf.startTime.Unix() //
	hour := diff / 3600
	return uint(hour)
}
func (bf *BloomFilter) Contains(value time.Time) bool {
	ret := true
	ret = ret && bf.set.Test(bf.Hash(value))
	return ret
}
func (bf *BloomFilter) String() string {
	return bf.set.String()
}

func (bf *BloomFilter) ToDate() []time.Time {
	dateStr := bf.String()
	timeStr := strings.Split(dateStr[1:len(dateStr)-1], ",")
	var tms []time.Time
	for _, str := range timeStr {
		unix, err := strconv.Atoi(str)
		if err != nil {
			continue
		}
		tm := time.Unix(int64(uint(unix*3600))+bf.startTime.Unix(), 0)
		tms = append(tms, tm)
	}
	return tms
}
func (bf *BloomFilter) Append(n *BloomFilter) {
	if n == nil {
		return
	}
	dateStr := n.String()
	timeStr := strings.Split(dateStr[1:len(dateStr)-1], ",")
	for _, str := range timeStr {
		unix, err := strconv.Atoi(str)
		if err != nil {
			continue
		}
		bf.set.Set(uint(unix))
	}
}
func (bf *BloomFilter) MarshalJSON() ([]byte, error) {
	return bf.set.MarshalJSON()
}
func (bf *BloomFilter) UnmarshalJSON(data []byte) error {
	bf.set = bitset.New(0)
	t1, err := time.ParseInLocation("2006-01-02 15:04:05", "2019-01-01 00:00:00", time.Local)
	if err != nil {
		t1 = time.Now()
	}
	bf.startTime = t1
	return bf.set.UnmarshalJSON(data)
}

type Demo struct {
	Bf   *BloomFilter `json:"bf"`
	Name string       `json:"name"`
}

func main() {
	//hash种子
	filter := NewBloomFilter(1)
	filter2 := NewBloomFilter(1)
	str1 := time.Now().AddDate(0, 0, 1)
	filter.Add(str1)
	str2 := time.Now().AddDate(0, 0, 2)
	filter.Add(str2)
	str3 := time.Now().AddDate(0, 0, 3)
	filter.Add(str3)
	filter2.Add(time.Now().AddDate(0, 0, 5))
	fmt.Println(filter.Contains(str1))
	fmt.Println(filter.Contains(str2))
	fmt.Println(filter.Contains(str3))
	fmt.Println(filter.Contains(time.Now().AddDate(0, 0, 4)))
	fmt.Println(filter.ToDate())
	fmt.Println(filter)

	dd := &Demo{
		Bf:   filter,
		Name: "ccc",
	}
	data, err := json.Marshal(dd)
	if err != nil {

	}
	fmt.Println("--------")
	fmt.Println(string(data))

	var d2 *Demo
	_ = json.Unmarshal(data, &d2)
	fmt.Println(d2.Bf.String())

	filter.Append(filter2)
	fmt.Println(filter)

	//
	//data, _ := filter.set.MarshalBinary()
	//_ = ioutil.WriteFile("./demo.txt", data, os.ModeAppend)

	//data, _ := ioutil.ReadFile("./demo.txt")
	//bytes.NewBuffer(data)
	//bit := bitset.New(0)
	//count, err := bit.ReadFrom(bytes.NewBuffer(data))
	//if err != nil {
	//	panic(err)
	//}
	//fmt.Println(count)
	//
	//fmt.Println(bit.String())
}
